﻿/***************************************************************************
 //* Foxit Software Inc.
 //* Copyright(C) 2005-2012, all rights reserved.
 //*
 //* The following code is copyrighted and contains proprietary information
 //* and trade secrets of Foxit Software Inc.                                                      *
 ***************************************************************************/

/**
 * 工具函数
 */
define(function (require, exports, module) {
    var WebPDF = require('./WebPDF.js');
    var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var base64DecodeChars = [
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1];

    /**
     * @namespace
     * @memberOf WebPDF
     * @description Common tools for JavaScript.
     */
    WebPDF.Common =
    {
        /**
         * Enum for Unit.
         * @enum
         * @private
         */
        Unit: {
            POINT: "point",
            INCH: "inch",
            CM: "cm",
            PICA: "pica"
        },
        /**
         * @description Prevent event bubble.
         * @param {Object} e The event.
         * @param {Boolean} selection Whether clear selection of the DOM.
         */
        preventDefaults: function (e, selection) {
            if (WebPDF.Environment.mobile)
                return;
            if (!e) {
                e = window.event;
            }
            if (e.preventDefault) {
                e.preventDefault();
            }
            try {
                if (selection && document.selection) {
                    document.selection.empty();
                }
            }
            catch (ex) {
                console.error(ex);
            }
        },
        getHiddenParamValue: function (key) {
            var elem = document.getElementById(key);
            if (elem) {
                return elem.value;
            }
            return null;
        },
        queryString: function (key) {
            return (document.location.search.match(new RegExp("(?:^\\?|&)" + key + "=(.*?)(?=&|$)")) || ['', null])[1];
        },

        /**
         * @description Insert a new html element after a element.
         * @param {Element} newElement New element to insert.
         * @param {Element} targetElement Target element.
         * @return {Element} A reference to the new element.
         */
        insertAfter: function (newElement, targetElement) {
            var parent = targetElement.parentNode;
            var oElement = null;
            if (parent.lastChild == targetElement) {
                oElement = parent.appendChild(newElement);
            }
            else {
                oElement = parent.insertBefore(newElement, targetElement.nextSibling);
            }
            return oElement;
        },
        /**
         * @description Convert timestamp to date.
         * @param {Object} nTimestamp Timestamp.
         * @return {Date} The date of the timestamp.
         */
        timestamp2Date: function (nTimestamp) {
            var d = new Date();
            return new Date((nTimestamp - d.getTimezoneOffset() * 60) * 1000); // 乘于1000是转为毫秒
        },
        /**
         * @description UTC date to local date.
         * @param {Object} nTimestamp UTC timestamp.
         * @param {String} format Format.
         * @return {Date} Local date.
         */
        timestamp2DateString: function (nTimestamp, format) {
            var d = new Date();
            var date = new Date((nTimestamp - d.getTimezoneOffset() * 60) * 1000);
            return this.formatDate(date, format);
        },
        AnnotTimeFormat: "M/d/yyyy hh:mm:ss",

        // 判断十六进制颜色是否偏黑，三分量相加的值是否超过255*3/2,如果超出返回false，表示偏白，否则返回true，表示偏黑
        // szColor的格式需为 #ffffff的形式
        isColorBlack: function (color) {
            var tmpColor = color.toLowerCase();
            if (tmpColor) {
                // 处理六位的颜色值
                //var colorChange = [];
                var totalColor = 0;
                for (var i = 1; i < 7; i += 2) {
                    totalColor += parseInt("0x" + tmpColor.slice(i, i + 2));
                }
                return totalColor * 2 < 255 * 3;
            } else {
                return true;
            }
        },
        // html 文本转义
        htmlEncode: function (str) {
            str = str.replace(/&/g, '&amp;');
            str = str.replace(/</g, '&lt;');
            str = str.replace(/>/g, '&gt;');
            str = str.replace(/"/g, '&quot;');
            str = str.replace(/'/g, '&#039;');
            return str;
        },
        // html文本转义，支持更多字符
        htmlEncodeEx: function (str) {
            var s = "";
            if (!str || str.length == 0) return "";
            for (var i = 0; i < str.length; i++) {
                switch (str.substr(i, 1)) {
                    case "<":
                        s += "&lt;";
                        break;
                    case ">":
                        s += "&gt;";
                        break;
                    case "&":
                        s += "&amp;";
                        break;
                    case " ":
                        if (str.substr(i + 1, 1) == " ") {
                            s += " &nbsp;";
                            i++;
                        } else s += " ";
                        break;
                    case "\"":
                        s += "&quot;";
                        break;
                    case "\n":
                        s += "<br>";
                        break;
                    default:
                        s += str.substr(i, 1);
                        break;
                }
            }
            return s;
        },
        // 时间格式化
        formatDate: function (date, format) {
            var o = {
                "M+": date.getMonth() + 1, // month
                "d+": date.getDate(), // day
                "h+": date.getHours(), // hour
                "m+": date.getMinutes(), // minute
                "s+": date.getSeconds(), // second
                "q+": Math.floor((date.getMonth() + 3) / 3), // quarter
                "S": date.getMilliseconds() // millisecond
            };

            if (/(y+)/.test(format)) {
                format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
            }

            for (var k in o) {
                if (new RegExp("(" + k + ")").test(format)) {
                    format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
                }
            }
            return format;
        },
        hexColor2Rgba: function (val, alpha) {
            var r=0, g=0, b=0;
            // 参数为RGB模式时不做进制转换，直接截取字符串即可
            if (/rgb/.test(val)) {
                var arr = val.match(/\d+/g);
                r = parseInt(arr[0]);
                g = parseInt(arr[1]);
                b = parseInt(arr[2]);
            }
            // 参数为十六进制时需要做进制转换
            else if (/#/.test(val)) {
                var len = val.length;
                // 非简写模式 #0066cc
                if (len === 7) {
                    r = parseInt(val.slice(1, 3), 16);
                    g = parseInt(val.slice(3, 5), 16);
                    b = parseInt(val.slice(5), 16);
                }
                // 简写模式 #06c
                else if (len === 4) {
                    r = parseInt(val.charAt(1) + val.charAt(1), 16);
                    g = parseInt(val.charAt(2) + val.charAt(2), 16);
                    b = parseInt(val.charAt(3) + val.charAt(3), 16);
                }
            }
            else {
                return val;
            }
            return "rgba(" + r + "," + g + "," + b + "," + alpha + ")";
        },
        /**
         * @description Update state of a button.
         * @param {String} btnId ID of the button.
         * @param {String} norClass  CSS styles of the button in normal state.
         * @param {String} disableClass CSS styles of the button in disable state .
         * @param {Boolean} bEnable Whether to enable the button.
         * @private
         */
        updateBtnState: function (btnId, norClass, disableClass, bEnable) {
            if (!bEnable) {
                $("#" + btnId).removeClass(norClass).addClass(disableClass).removeClass("fwr_toolbar_btn");
            }
            else {
                $("#" + btnId).removeClass(disableClass).addClass(norClass).addClass("fwr_toolbar_btn");
            }
        },
        changeUnit: function (fSrc, srcType, destType) {
            var fInch = 0.0;
            var Unit = WebPDF.Common.Unit;
            switch (srcType) {
                case Unit.POINT:
                    fInch = fSrc / 72.0;
                    break;
                case Unit.INCH:
                    fInch = fSrc;
                    break;
                case Unit.CM:
                    fInch = fSrc / 2.54;
                    break;
                case Unit.PICA:
                    fInch = fSrc / 6.0;
                    break;
                default:
                    break;
            }

            switch (destType) {
                case Unit.POINT:
                    fInch *= 72.0;
                    break;
                case Unit.INCH:
                    break;
                case Unit.CM:
                    fInch *= 2.54;
                    break;
                case UNIT.PICA:
                    fInch *= 6.0;
                    break;
                default:
                    break;
            }
            return fInch;
        },
        parseValueToDecimal: function (value) {
            if (!value) {
                return false;
            }
            var fValue = Math.round(value * 100) / 100;
            var strValue = fValue.toString();
            var posDecimal = strValue.indexOf('.');
            if (posDecimal < 0) {
                posDecimal = strValue.length;
                strValue += '.';
            }
            while (strValue.length <= posDecimal + 2) {
                strValue += '0';
            }
            return strValue;
        },
        getUnicodeStringLength: function (unicodeStr) {
            var byteLen = 0;
            var len = unicodeStr.length;
            if (unicodeStr) {
                for (var i = 0; i < len; i++) {
                    if (str.charCodeAt(i) > 255) {
                        byteLen += 2;
                    }
                    else {
                        byteLen++;
                    }
                }
                return byteLen;
            }
            return 0;
        },
        isNeedRetry: function (state) {
            return state == WebPDF.ConversionState.IN_PROGRESS
                || state == WebPDF.ConversionState.INITIALIZE;
        },
        containsNode: function (parent, child, isContainSelf) {
            if (isContainSelf && parent === child) {
                return true;
            }
            return $.contains(parent, child);
        },
        /**
         * @description Base-64 encoder.
         * @param {String} str The string to encode.
         */
        base64encode: function (str) {
            var out, i, len;
            var c1, c2, c3;

            len = str.length;
            i = 0;
            out = "";
            while (i < len) {
                c1 = str.charCodeAt(i++) & 0xff;
                if (i == len) {
                    out += base64EncodeChars.charAt(c1 >> 2);
                    out += base64EncodeChars.charAt((c1 & 0x3) << 4);
                    out += "==";
                    break;
                }
                c2 = str.charCodeAt(i++);
                if (i == len) {
                    out += base64EncodeChars.charAt(c1 >> 2);
                    out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
                    out += base64EncodeChars.charAt((c2 & 0xF) << 2);
                    out += "=";
                    break;
                }
                c3 = str.charCodeAt(i++);
                out += base64EncodeChars.charAt(c1 >> 2);
                out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
                out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
                out += base64EncodeChars.charAt(c3 & 0x3F);
            }
            return out;
        },
        /**
         * @description Base-64 decoder.
         * @param {String} str The string to decode.
         */
        base64decode: function (str) {
            var c1, c2, c3, c4;
            var i, len, out;

            len = str.length;
            i = 0;
            out = "";
            while (i < len) {
                /* c1 */
                do {
                    c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
                } while (i < len && c1 == -1);
                if (c1 == -1)
                    break;

                /* c2 */
                do {
                    c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
                } while (i < len && c2 == -1);
                if (c2 == -1)
                    break;

                out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));

                /* c3 */
                do {
                    c3 = str.charCodeAt(i++) & 0xff;
                    if (c3 == 61)
                        return out;
                    c3 = base64DecodeChars[c3];
                } while (i < len && c3 == -1);
                if (c3 == -1)
                    break;

                out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));

                /* c4 */
                do {
                    c4 = str.charCodeAt(i++) & 0xff;
                    if (c4 == 61)
                        return out;
                    c4 = base64DecodeChars[c4];
                } while (i < len && c4 == -1);
                if (c4 == -1)
                    break;
                out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
            }
            return out;
        },
        // this is a fix for the jQuery slide effects
        slideToggle: function (el, bShow) {
            var $el = $(el), height = $el.data("originalHeight"), visible = $el.is(":visible");

            // if the bShow isn't present, get the current visibility and reverse it
            if (arguments.length == 1) bShow = !visible;

            // if the current visibility is the same as the requested state, cancel
            if (bShow == visible) return false;

            // get the original height
            if (!height) {
                // get original height
                height = $el.show().height();
                // update the height
                $el.data("originalHeight", height);
                // if the element was hidden, hide it again
                if (!visible) $el.hide().css({height: 0});
            }

            // expand the knowledge (instead of slideDown/Up, use custom animation which applies fix)
            if (bShow) {
                $el.show().animate({height: height}, {duration: 250});
            } else {
                $el.animate({height: 0}, {duration: 250, complete: function () {
                    $el.hide();
                }
                });
            }
        },
        /**
         * Check whether the element is interactive.
         * @param readerApp The current app instance.
         * @param elem The element to check
         * @private
         */
        isElemInteractive: function (readerApp, elem) {
            var $elem = $(elem);
            var elemType = $elem.attr("type") ? $elem.attr("type") : elem.type;
            if (!elemType) {
                elemType = "";
            }
            else {
                elemType = elemType.toLowerCase();
            }
            var nodeName = elem.nodeName.toLowerCase();
            if (elemType === 'text' || elemType === 'textarea' || elemType === 'select-one'
                || elem.tagName.toLowerCase() === 'option' || nodeName==='select' 
                || nodeName==='input' || elem.isContentEditable) {
                return true;
            }
            var ignoreMouseEventClasses = readerApp.getIgnoreMouseEventClasses();
            var isIgnoreElem = false;
            $.each(ignoreMouseEventClasses,function(n, value) {
                if ($elem.hasClass(value) || $elem.parents("." + value).length > 0) {
                    isIgnoreElem = true;
                    return true;
                }
            });
            if(isIgnoreElem){
                return true;
            }
            var events = $.data(elem, "events");
            if (events && !(Object.keys(events).length == 1 && events["contextmenu"])) {
                 return true;
            }
            return false;
        },
        /**
         * Create a unique id.
         * @returns {String}
         */
        createUniqueId: function () {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        },
        vectorLength: function(point){
            return Math.sqrt(point.x * point.x + point.y * point.y);
        },
        slopeAngle: function (point) {
            return WebPDF.Common.arcCosine(point, new WebPDF.PDFPoint(1.0, 0.0));
        },
        arcCosine: function (pointA, pointB) {
            return Math.acos(WebPDF.Common.cosine(pointA, pointB));
        },
        cosine: function (pointA, pointB) {
            var dotProduct = WebPDF.Common.dotProduct(pointA, pointB);
            return dotProduct / (WebPDF.Common.vectorLength(pointA) * WebPDF.Common.vectorLength(pointB));
        },
        dotProduct: function (pointA, pointB) {
            return pointA.x * pointB.x + pointA.y * pointB.y;
        },
        getSign: function (a) {
            return (a > 0 ? 1 : a < 0 ? -1 : 0);
        },
        clone: function (obj) {
            var clone;

            // Array.
            if (obj && (obj instanceof Array)) {
                clone = [];
                for (var i = 0; i < obj.length; i++)
                    clone[i] = this.clone(obj[i]);

                return clone;
            }

            // "Static" types.
            if (obj === null
                || (typeof (obj) != 'object')
                || (obj instanceof String)
                || (obj instanceof Number)
                || (obj instanceof Boolean)
                || (obj instanceof Date)
                || (obj instanceof RegExp)) {
                return obj;
            }

            // Objects.
            clone = new obj.constructor();

            for (var propertyName in obj) {
                var property = obj[propertyName];
                clone[propertyName] = this.clone(property);
            }

            return clone;
        }
    };

    return WebPDF.Common;
});
